package com.jushu.storbox.util.image;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.SoftReference;
import java.net.URL;
import java.net.URLEncoder;
import java.util.List;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.media.ExifInterface;
import android.os.Environment;
import android.os.StatFs;

import com.jushu.storbox.ShareDriveApplication;

public class BitmapUtil
{
    /**
     * @param context
     * @param resId
     * @return
     */
    public static Bitmap ReadBitmapById(Context context, int resId)
    {
        try
        {
            BitmapFactory.Options opt = new BitmapFactory.Options();
            opt.inPreferredConfig = Bitmap.Config.RGB_565;
            opt.inPurgeable = true;
            opt.inInputShareable = true;
            InputStream is = null;
            Bitmap bmp = null;
            try
            {
                is = context.getResources().openRawResource(resId);
                bmp = BitmapFactory.decodeStream(is, null, opt);
            }
            catch (Exception e)
            {
                // TODO: handle exception
            }
            finally
            {
                try
                {
                    if (null != is)
                    {
                        is.close();
                    }
                }
                catch (Exception e2)
                {
                    // TODO: handle exception
                    e2.printStackTrace();
                }
            }
            return bmp;
        }
        catch (Exception e)
        {
            // TODO: handle exception
            e.printStackTrace();
        }
        return null;
    }
    
    /***
     * @param context
     * @param drawableId
     * @return
     */
    public static Bitmap ReadBitmapById(Context context, int drawableId, int screenWidth, int screenHight)
    {
        try
        {
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inPreferredConfig = Config.ARGB_8888;
            options.inInputShareable = true;
            options.inPurgeable = true;
            
            InputStream stream = null;
            Bitmap bmp = null;
            try
            {
                stream = context.getResources().openRawResource(drawableId);
                Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
                bmp = getBitmap(bitmap, screenWidth, screenHight);
            }
            catch (Exception e)
            {
                // TODO: handle exception
                e.printStackTrace();
            }
            finally
            {
                try
                {
                    if (null != stream)
                    {
                        stream.close();
                    }
                }
                catch (Exception e2)
                {
                    // TODO: handle exception
                    e2.printStackTrace();
                }
            }
            return bmp;
        }
        catch (Exception e)
        {
            // TODO: handle exception
            e.printStackTrace();
        }
        return null;
    }
    
    /***
     * @param bitmap
     * @param screenWidth
     * @param screenHight
     * @return
     */
    public static Bitmap getBitmap(Bitmap bitmap, int screenWidth, int screenHight)
    {
        try
        {
            if (null == bitmap)
            {
                return null;
            }
            int w = bitmap.getWidth();
            int h = bitmap.getHeight();
            Matrix matrix = new Matrix();
            float scale = (float) screenWidth / w;
            float scale2 = (float) screenHight / h;
            
            scale = scale < scale2 ? scale : scale2;
            
            matrix.postScale(scale, scale);
            return Bitmap.createBitmap(bitmap, 0, 0, w, h, matrix, true);
        }
        catch (Exception e)
        {
            // TODO: handle exception
            e.printStackTrace();
        }
        return null;
    }
    
    /***
     * @param bm
     * @param url
     * @param quantity
     */
    private static int FREE_SD_SPACE_NEEDED_TO_CACHE = 1;
    
    private static int MB = 1024 * 1024;
    
    public final static String DIR = "/sdcard/hypers";
    
    public static void saveBmpToSd(Bitmap bm, String url, int quantity)
    {
        try
        {
            if (FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd())
            {
                return;
            }
            if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()))
                return;
            if (null == bm)
            {
                return;
            }
            String filename = url;
            File dirPath = new File(DIR);
            if (!dirPath.exists())
            {
                dirPath.mkdirs();
            }
            
            File file = new File(DIR + "/" + filename);
            OutputStream outStream = null;
            try
            {
                file.createNewFile();
                outStream = new FileOutputStream(file);
                bm.compress(Bitmap.CompressFormat.PNG, quantity, outStream);
                outStream.flush();
            }
            catch (FileNotFoundException e)
            {
                e.printStackTrace();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
            finally
            {
                try
                {
                    if (null != outStream)
                    {
                        outStream.close();
                    }
                }
                catch (Exception e2)
                {
                    // TODO: handle exception
                    e2.printStackTrace();
                }
            }
        }
        catch (Exception e)
        {
            // TODO: handle exception
            e.printStackTrace();
        }
    }
    
    /***
     * @param url
     * @param quantity
     * @return
     */
    public static Bitmap GetBitmap(String url, int quantity)
    {
        try
        {
            InputStream inputStream = null;
            String filename = "";
            Bitmap map = null;
            URL url_Image = null;
            String LOCALURL = "";
            if (url == null)
                return null;
            try
            {
                filename = url;
            }
            catch (Exception err)
            {
            }
            
            LOCALURL = URLEncoder.encode(filename);
            if (Exist(DIR + "/" + LOCALURL))
            {
                map = BitmapFactory.decodeFile(DIR + "/" + LOCALURL);
            }
            else
            {
                try
                {
                    url_Image = new URL(url);
                    inputStream = url_Image.openStream();
                    map = BitmapFactory.decodeStream(inputStream);
                    // url = URLEncoder.encode(url, "UTF-8");
                    if (map != null)
                    {
                        saveBmpToSd(map, LOCALURL, quantity);
                    }
                    inputStream.close();
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                    // return null;
                    map = null;
                }
                finally
                {
                    try
                    {
                        if (null != inputStream)
                        {
                            inputStream.close();
                        }
                    }
                    catch (Exception e2)
                    {
                        // TODO: handle exception
                        e2.printStackTrace();
                    }
                }
            }
            return map;
        }
        catch (Exception e)
        {
            // TODO: handle exception
            e.printStackTrace();
        }
        return null;
    }
    
    /***
     * 
     * @param url
     * @return
     */
    public static boolean Exist(String url)
    {
        File file = new File(DIR + url);
        return file.exists();
    }
    
    private static int freeSpaceOnSd()
    {
        StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());
        double sdFreeMB = ((double) stat.getAvailableBlocks() * (double) stat.getBlockSize()) / MB;
        
        return (int) sdFreeMB;
    }
    
    public static InputStream byteToInputStream(byte[] data) throws IOException
    {
        InputStream is = new ByteArrayInputStream(data);
        try
        {
            if (null != is)
            {
                is.close();
            }
        }
        catch (Exception e)
        {
            // TODO: handle exception
            e.printStackTrace();
        }
        return is;
    }
    
    public static Bitmap readBitMap(Context context, InputStream is, int screenWidth, int screenHight,
        int inSize)
    {
        // int maxMemory = (int) Runtime.getRuntime().maxMemory();
        // int totalMemory = (int) Runtime.getRuntime().totalMemory();
        // int freeMemory = (int) Runtime.getRuntime().freeMemory();
        BitmapFactory.Options opt = new BitmapFactory.Options();
        opt.inSampleSize = inSize;
        opt.inPreferredConfig = Config.ARGB_8888;
        opt.inPurgeable = true;
        opt.inInputShareable = true;
        Bitmap map = null;
        try
        {
            map = BitmapFactory.decodeStream(is, null, opt);
        }
        catch (Exception e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
            map = null;
        }
        finally
        {
            try
            {
                if (null != is)
                {
                    is.close();
                    is = null;
                }
            }
            catch (Exception e)
            {
                // TODO: handle exception
                e.printStackTrace();
            }
        }
        return getBitmap(map, screenWidth, screenHight);
    }
    
    public static Bitmap readBitMapFromFile(Context context, String filePath)
    {
        BitmapFactory.Options opt = new BitmapFactory.Options();
        opt.inPreferredConfig = Config.ARGB_8888;
        opt.inPurgeable = true;
        opt.inInputShareable = true;
        Bitmap map = BitmapFactory.decodeFile(filePath, opt);
        return map;
    }
    
    public synchronized static void recycleCloudBitmap()
    {
        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                // TODO Auto-generated method stub
                try
                {
                    List<SoftReference<Bitmap>> cloudBitmaps = ShareDriveApplication.getInstance()
                        .getCloudBitmaps();
                    int recycleSize = cloudBitmaps.size();
                    int n = 0;
                    if (null != cloudBitmaps && 0 < cloudBitmaps.size())
                    {
                        for (SoftReference<Bitmap> softReference : cloudBitmaps)
                        {
                            Bitmap bmp = softReference.get();
                            if (null != bmp && !bmp.isRecycled())
                            {
                                bmp.recycle();
                                System.gc();
                            }
                            else
                            {
                                
                            }
                            // ShareDriveApplication.getInstance().removeCloudBitmaps(bmp);
                            n++;
                            if (n >= recycleSize)
                            {
                                break;
                            }
                        }
                        if (n == ShareDriveApplication.getInstance().getCloudBitmaps().size())
                        {
                            ShareDriveApplication.getInstance().clearCloudBitmaps();
                        }
                    }
                    else
                    {
                        
                    }
                }
                catch (Exception e)
                {
                    // TODO: handle exception
                    e.printStackTrace();
                }
            }
        }).start();
    }
    
    public synchronized static void recycleSharedBitmap()
    {
        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                // TODO Auto-generated method stub
                try
                {
                    List<SoftReference<Bitmap>> sharedBitmaps = ShareDriveApplication.getInstance()
                        .getSharedBitmaps();
                    int recycleSize = sharedBitmaps.size();
                    int n = 0;
                    if (null != sharedBitmaps && 0 < sharedBitmaps.size())
                    {
                        for (SoftReference<Bitmap> softReference : sharedBitmaps)
                        {
                            Bitmap bmp = softReference.get();
                            if (bmp != null && !bmp.isRecycled())
                            {
                                bmp.recycle();
                                System.gc();
                            }
                            else
                            {
                            }
                            // ShareDriveApplication.getInstance().removeSharedBitmaps(bmp);
                            n++;
                            if (n >= recycleSize)
                            {
                                break;
                            }
                        }
                        if (n == ShareDriveApplication.getInstance().getSharedBitmaps().size())
                        {
                            ShareDriveApplication.getInstance().clearSharedBitmaps();
                        }
                    }
                    else
                    {
                        
                    }
                    
                }
                catch (Exception e)
                {
                    // TODO: handle exception
                    e.printStackTrace();
                }
                
            }
        }).start();
    }
    
    public synchronized static void recycleTempBitmap()
    {
        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                // TODO Auto-generated method stub
                try
                {
                    List<SoftReference<Bitmap>> tempBitmaps = ShareDriveApplication.getInstance()
                        .getTempBitmaps();
                    int recycleSize = tempBitmaps.size();
                    int n = 0;
                    if (null != tempBitmaps && 0 < tempBitmaps.size())
                    {
                        for (SoftReference<Bitmap> softReference : tempBitmaps)
                        {
                            Bitmap bmp = softReference.get();
                            if (bmp != null && !bmp.isRecycled())
                            {
                                bmp.recycle();
                                System.gc();
                            }
                            else
                            {
                            }
                            // ShareDriveApplication.getInstance().removeTempBitmaps(bmp);
                            n++;
                            if (n >= recycleSize)
                            {
                                break;
                            }
                        }
                        if (n == ShareDriveApplication.getInstance().getTempBitmaps().size())
                        {
                            ShareDriveApplication.getInstance().clearTempBitmaps();
                        }
                    }
                    else
                    {
                    }
                    
                }
                catch (Exception e)
                {
                    // TODO: handle exception
                    e.printStackTrace();
                }
                
            }
        }).start();
    }
    
    public synchronized static void recycleBitmapList(final List<SoftReference<Bitmap>> bitmaps)
    {
        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                // TODO Auto-generated method stub
                try
                {
                    int recycleSize = bitmaps.size();
                    int n = 0;
                    if (null != bitmaps && 0 < bitmaps.size())
                    {
                        for (SoftReference<Bitmap> softReference : bitmaps)
                        {
                            Bitmap bmp = softReference.get();
                            if (null != bmp && !bmp.isRecycled())
                            {
                                bmp.recycle();
                                System.gc();
                            }
                            else
                            {
                                
                            }
                            // ShareDriveApplication.getInstance().removeCloudBitmaps(bmp);
                            n++;
                            if (n >= recycleSize)
                            {
                                break;
                            }
                        }
                        if (n == recycleSize)
                        {
                            bitmaps.clear();
                        }
                    }
                    else
                    {
                        
                    }
                }
                catch (Exception e)
                {
                    // TODO: handle exception
                    e.printStackTrace();
                }
            }
        }).start();
    }
    
    public synchronized static void recycleBitmap(final Bitmap bmp)
    {
        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                // TODO Auto-generated method stub
                try
                {
                    if (bmp != null && !bmp.isRecycled())
                    {
                        bmp.recycle();
                        System.gc();
                    }
                }
                catch (Exception e)
                {
                    // TODO: handle exception
                    e.printStackTrace();
                }
            }
        }).start();
    }
    
    /***
     * 
     * @param path
     * @return
     */
    public static Bitmap GetBitmap(String path)
    {
        Bitmap map = null;
        if (path == null)
            return null;
        try
        {
            BitmapFactory.Options opts = new BitmapFactory.Options();
            opts.inPreferredConfig = Config.ARGB_8888;
            opts.inInputShareable = true;
            opts.inPurgeable = true;
            map = BitmapFactory.decodeFile(path, opts);
        }
        catch (Exception e)
        {
            // TODO: handle exception
            e.printStackTrace();
        }
        return map;
    }
    
    /**
     * @see declodeSampledBitmapFromFile
     * @param path
     * @param reqWidth
     * @param reqHeight
     * 
     * @return Bitmap
     * 
     * */
    public static Bitmap decodeSampledBitmapFromFile(String path, int reqWidth, int reqHeight)
    {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        
        BitmapFactory.decodeFile(path, options);
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(path, options);
    }
    
    private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight)
    {
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;
        if (height > reqHeight || width > reqWidth)
        {
            final int heightRatio = Math.round((float) height / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
            calculateInRatioSize(options, heightRatio, widthRatio);
        }
        return inSampleSize;
    }
    
    /**
     * Config.x ALPHA_8：per pix use 1byte memory ARGB_4444:per pix use 2byte memory ARGB_8888:per pix use 4byte memory
     * RGB_565:per pix use 2byte memory
     **/
    private static void calculateInRatioSize(BitmapFactory.Options options, int heightRatio, int widthRatio)
    {
        int multipRatio = heightRatio * widthRatio;
        if (multipRatio < 9)
        {
            options.inPreferredConfig = Config.ARGB_8888;
        }
        else
        {
            options.inPreferredConfig = Config.ARGB_4444;
        }
    }
    
    /**
     * @see declodeSampledBitmapFromFile
     * @param path
     * @param reqWidth 
     * @param reqHeight 
     * 
     * @return Bitmap
     * 
     * */
    public static Bitmap createImageThumbnail(String filePath, int reqWidth, int reqHeight)
    {
        File file = new File(filePath);
        if (!file.exists())
        {
            return null;
        }
        Bitmap bitmap = null;
        BitmapFactory.Options opts = new BitmapFactory.Options();
        opts.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(filePath, opts);
        int inSampleSize = computeSampleSize(opts, -1, reqWidth * reqHeight);// 128*128
        opts.inSampleSize = inSampleSize;
        opts.inJustDecodeBounds = false;
        
        try
        {
            bitmap = BitmapFactory.decodeFile(filePath, opts);
            if (null != bitmap)
            {
                int degree = BitmapUtil.getBitmapDegree(filePath);
                if (0 != degree)
                {
                    bitmap = BitmapUtil.rotateBitmapByDegree(bitmap, degree);
                }
            }
        }
        catch (Exception e)
        {
            // TODO: handle exception
            e.printStackTrace();
        }
        return bitmap;
    }
    
    private static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels)
    {
        int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels);
        int roundedSize;
        if (initialSize <= 8)
        {
            roundedSize = 1;
            while (roundedSize < initialSize)
            {
                roundedSize <<= 1;
            }
        }
        else
        {
            roundedSize = (initialSize + 7) / 8 * 8;
        }
        return roundedSize;
    }
    
    private static int computeInitialSampleSize(BitmapFactory.Options options, int minSideLength,
        int maxNumOfPixels)
    {
        double w = options.outWidth;
        double h = options.outHeight;
        int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
        int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(Math.floor(w / minSideLength),
            Math.floor(h / minSideLength));
        if (upperBound < lowerBound)
        {
            // return the larger one when there is no overlapping zone.
            return lowerBound;
        }
        if ((maxNumOfPixels == -1) && (minSideLength == -1))
        {
            return 1;
        }
        else if (minSideLength == -1)
        {
            return lowerBound;
        }
        else
        {
            return upperBound;
        }
    }
    
    /**
     * @param path 
     * @return
     */
    public static int getBitmapDegree(String path)
    {
        int degree = 0;
        try
        {
            ExifInterface exifInterface = new ExifInterface(path);
            // Get image rotate info
            int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                ExifInterface.ORIENTATION_NORMAL);
            switch (orientation)
            {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    degree = 90;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    degree = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    degree = 270;
                    break;
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        return degree;
    }
    
    /**
     * @param bm  
     *         Image need rotate
     * @param degree 
     * @return  result image
     */
    public static Bitmap rotateBitmapByDegree(Bitmap bm, int degree)
    {
        Bitmap returnBm = null;
        Matrix matrix = new Matrix();
        matrix.postRotate(degree);
        try
        {
            returnBm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
        }
        catch (OutOfMemoryError e)
        {
            e.printStackTrace();
        }
        if (returnBm == null)
        {
            returnBm = bm;
        }
        if (bm != returnBm)
        {
            bm.recycle();
        }
        return returnBm;
    }


	/**
	 * @param bitmap
	 * @param f
	 * @return
	 */
	public static Bitmap zoom(Bitmap bitmap, float zf) {
		Matrix matrix = new Matrix();
		matrix.postScale(zf, zf);
		return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
				bitmap.getHeight(), matrix, true);
	}

	/**
	 * @param bitmap
	 * @param f
	 * @return
	 */
	public static Bitmap zoom(Bitmap bitmap, float wf, float hf) {
		Matrix matrix = new Matrix();
		matrix.postScale(wf, hf);
		return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
				bitmap.getHeight(), matrix, true);
	}

	/**
	 * @param bitmap
	 * @param roundPX
	 * @return
	 */
	public static Bitmap getRCB(Bitmap bitmap, float roundPX) {
		// RCB means
		// Rounded
		// Corner Bitmap
		Bitmap dstbmp = Bitmap.createBitmap(bitmap.getWidth(),
				bitmap.getHeight(), Config.ARGB_8888);
		Canvas canvas = new Canvas(dstbmp);

		final int color = 0xff424242;
		final Paint paint = new Paint();
		final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
		final RectF rectF = new RectF(rect);
		paint.setAntiAlias(true);
		canvas.drawARGB(0, 0, 0, 0);
		paint.setColor(color);
		canvas.drawRoundRect(rectF, roundPX, roundPX, paint);
		paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
		canvas.drawBitmap(bitmap, rect, rect, paint);
		return dstbmp;
	}
}
